package org.wlgzs.office_exam_student.service;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wlgzs.office_exam_student.service.office.FinishedCall;
import org.wlgzs.office_exam_student.service.office.OfficeConfig;
import org.wlgzs.office_exam_student.service.office.OfficeResolver;
import org.wlgzs.office_exam_student.service.office.ResolveResult;
import org.wlgzs.office_exam_student.service.office.ResolveTask;

/**
*@author song(mejeesong@qq.com)
*2018年1月25日
*设置与获得officeResolver
*/
public class OfficeResolverService {
	
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private final Map<String,Class<? extends org.wlgzs.office_exam_resolver.OfficeResolver>> resolvers = new ConcurrentHashMap<>();
	
	private volatile LinkedBlockingQueue<ResolveTask> taskQueue = null;
	private AtomicLong currId = new AtomicLong(1);
	private volatile long currResolveId = 1;
	private ExecutorService es = null;
    private ExecutorService resoleWorker = null;
	
	
	public OfficeResolverService(int maxTask,int resolveThread) {
		taskQueue = new LinkedBlockingQueue<>(maxTask);
		es = Executors.newFixedThreadPool(resolveThread);
        resoleWorker = Executors.newFixedThreadPool(resolveThread);
		Thread thread = new Thread(new ResolveWorker());
		thread.start();
		List<org.wlgzs.office_exam_resolver.OfficeResolver> rs = new LinkedList<>();
		String[] packStr = {"","one","two"};
		String[] cnameStr = {"","One","Two"};
		String pack = "org.wlgzs.office_exam_resolver.impl.";
		for(int j=1;j<3;j++) {
			for(int i=1;i<9;i++) {
				StringBuilder sb = new StringBuilder().append(pack).append("excel.").append(packStr[j]);
				sb.append(".OfficeResolver").append(cnameStr[j]).append("Excel").append(i);
				loadResolver(sb.toString(),rs);
				sb = new StringBuilder().append(pack).append("word.").append(packStr[j]);
				sb.append(".OfficeResolver").append(cnameStr[j]).append("Word").append(i);
				loadResolver(sb.toString(),rs);
				sb = new StringBuilder().append(pack).append("ppt.").append(packStr[j]);
				sb.append(".OfficeResolver").append(cnameStr[j]).append("Ppt").append(i);
				loadResolver(sb.toString(),rs);
			}
		}
		for(org.wlgzs.office_exam_resolver.OfficeResolver or:rs) {
			//防止写解析器的人返回null
			if(or != null && or.getKey() != null) {
				registerResolver(or.getKey(), or.getClass());
			}
		}
	}
	private static void loadResolver(String cname,List<org.wlgzs.office_exam_resolver.OfficeResolver> rs) {
		try {
			@SuppressWarnings("unchecked")
			Class<? extends org.wlgzs.office_exam_resolver.OfficeResolver> c = (Class<? extends org.wlgzs.office_exam_resolver.OfficeResolver>) OfficeResolverService.class.getClassLoader().loadClass(cname);
			if(c==null)
				return;
			org.wlgzs.office_exam_resolver.OfficeResolver r = c.newInstance();
			rs.add(r);
		} catch (Exception e) {
			//e.printStackTrace();
		}
	}
	
	/**
	*@author song(mejeesong@qq.com)
	*@date 2018年1月30日
	*@param task
	*@return
	*添加解析任务，返回代表任务的Id,若队列已满，返回 -1
	*/
	public long addTask(ResolveTask task) {
		task.setId(currId.getAndIncrement());
		boolean succ = taskQueue.offer(task);
		return succ?task.getId():-1;
	}
	
	/**
	*@author song(mejeesong@qq.com)
	*@date 2018年1月30日
	*@param taskId
	*@return
	*查询前面的任务数量，返回 < 0 则代表任务已经完成
	*/
	public long queryFront(long taskId) {
		long num =  taskId - currResolveId;
		return num;
	}
	
	/**
	*@author song(mejeesong@qq.com)
	*@date 2018年1月30日
	*@param key
	*@param resolver
	*注册解析器，每一个操作题提供一个 OfficeResolver 实现类，每个题key唯一
	*/
	public void registerResolver(String key,Class<? extends org.wlgzs.office_exam_resolver.OfficeResolver> resolver) {
		logger.debug("registerResolver:"+String.valueOf(key));
		resolvers.put(key, resolver);
	}
	
	/**
	*@author song(mejeesong@qq.com)
	*@date 2018年1月30日
	*@param key
	*@return
	*通过key获得解析器
	*/
	public OfficeResolver getResolver(String key) {
		try {
			Class<? extends org.wlgzs.office_exam_resolver.OfficeResolver> c = resolvers.get(key);
			if(c != null) {
				org.wlgzs.office_exam_resolver.OfficeResolver or = c.newInstance();
				if(or != null) {
					return OfficeResolver.generateResolver(or);
				}
			}
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}

	class ResolveTaskWroker implements Callable<ResolveResult>{

	    private ResolveTask task =null;

	    ResolveTaskWroker(ResolveTask task){
	        this.task = task;
        }

        @Override
        public ResolveResult call() throws Exception {
            ResolveResult result = null;
            if(task != null) {
                logger.debug("开始解析任务："+task);
                OfficeResolver resolver = OfficeResolverService.this.getResolver(task.getFileKey());
                if(resolver != null) {
                    try {
                        result = resolver.resolve(OfficeConfig.resolveFileDir(task));
                        logger.debug("解析任务完成，任务{},结果:{}", task, result);
                    } catch (Exception e) {
                        logger.debug("解析任务出现异常，任务{}", task);
                        e.printStackTrace();
                    }
                }else {
                    logger.debug("找不到解析器:{}",task);
                }
            }
            currResolveId = task.getId();
            if(result == null){
                result = new ResolveResult(0,0, Arrays.asList("解析出现错误或者超时。"));
            }
            task.setResolveResult(result);
            FinishedCall call = task.getFinshedCall();
            if(call != null){
                call.onFinished(result);
            }
            return result;
        }
    }

	class ResolveATask implements Callable<ResolveResult> {

	    private ResolveTask task;

	    ResolveATask(ResolveTask task){
	        this.task = task;
        }

        @Override
        public ResolveResult call() throws Exception {
            Future<ResolveResult> f = resoleWorker.submit(new ResolveTaskWroker(task));
            ResolveResult rr = null;
            try{
                rr = f.get(12,TimeUnit.SECONDS);
            }catch (Exception e){
                e.printStackTrace();
            }
            return rr;
        }
    }
	class ResolveWorker implements Runnable{
		
		@Override
		public void run() {
		    while (true){
                try {
                    ResolveTask task = taskQueue.take();
                    es.submit(new ResolveATask(task));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
		}
	}
}
